<template>
  <div>
    <el-upload
      v-bind="$attrs"
      v-on="$listeners"
      :action="action"
      :http-request="httpRequest"
      :auto-upload="selfAutoUpload"
      :on-success="selfOnSuccess"
      :on-change="selfOnChange"
      :on-remove="selfOnRemove"
      :on-preview="selfOnPreview"
      :before-upload="selfBeforeUpload"
      :list-type="selfListType"
      :file-list="fileList"
      :show-file-list="showFileList"
    >
      <div v-if="isSingleImagePreviewType" class="single-wrap">
        <img class="single-image" :src="singleImageObj.url" alt="" />
        <div class="single-mask"></div>
        <div class="single-actions">
          <div class="action-warp" v-if="singleImageObj.url && isPut">
            <div class="action-warp-icon" @click.stop="handleSingleImageObjPreview">
              <i class="action-icon el-icon-zoom-in"></i>
            </div>
            <div class="action-warp-icon" @click.stop="handleDelete">
              <i class="action-icon el-icon-delete"></i>
            </div>
            <div class="action-warp-icon" @click.stop="downLoad(singleImageObj.url)">
              <i class="action-icon el-icon-download"></i>
            </div>
            <div class="action-warp-icon">
              <i class="action-icon el-icon-plus"></i>
            </div>
          </div>
          <span v-else>
            <i class="action-icon el-icon-plus"></i>
          </span>
        </div>
      </div>

      <slot name="trigger">
        <i v-if="selfListType === 'picture-card'" class="el-icon-plus"></i>
        <el-button v-else type="primary">{{ triggerText }}</el-button>
      </slot>
    </el-upload>
  </div>
</template>
<script>
import { msgbox } from '@/common/utils/common'
function noop() {}
export default {
  name: 'UploadSeal',
  components: {},
  props: {
    action: {
      type: String,
      default: '',
    },
    singleImage: Boolean,
    // 传入字符串，则直接使用该字符串作为文件名，否则
    // 0 -> file.name, 1 -> tuid, 2 -> guid
    fileName: {
      type: [String, Number],
      default: 1,
    },

    // 图片是否压缩
    compress: {
      type: Boolean,
      default: true,
    },
    // 图片压缩参数
    compressOptions: {
      type: Object,
      default() {
        return {
          width: 1920,
          quality: 0.8,
        }
      },
    },

    // 全部上传完成
    onComplete: { type: Function, default: noop },

    // 有冲突的attrs，需要截胡
    triggerText: { type: String, default: '点击上传' },
    fileList: Array,
    autoUpload: Boolean,
    cfcaUserId: String,
    isPut: { type: Boolean, default: false },
    listType: { type: String, default: 'text' },
    beforeUpload: { type: Function, default: noop },
    onChange: { type: Function, default: noop },
    onRemove: { type: Function, default: noop },
    onPreview: { type: Function, default: noop },
    onSuccess: { type: Function, default: noop },
    showFileList: { type: Boolean, default: true },
  },
  data() {
    return {
      hasRemoveAction: false,
      uploadList: [],
      // 预览
      singleImageObj: {},
    }
  },
  computed: {
    // 修改 el-upload 的默认值，默认为手动上传
    selfAutoUpload() {
      return this.autoUpload || false
    },
    selfListType() {
      return this.singleImage ? 'picture-card' : this.listType
    },
    isSingleImagePreviewType() {
      return this.singleImage
    },
    isImageType() {
      return ['picture-card', 'picture'].includes(this.selfListType)
    },
  },
  watch: {
    fileList: {
      handler: function (val) {
        const $el = this.$children[0]
        $el && $el.clearFiles()
        this.uploadList = []
        this.getSingleImageObj(val)
      },
      deep: true,
      immediate: true,
    },
  },
  created() {},
  methods: {
    downLoad(imgbase) {
      const aLink = document.createElement('a')
      aLink.download = '公章.png'
      const blob = this.base64ToBlob(imgbase)
      aLink.href = URL.createObjectURL(blob)
      aLink.click()
    },
    base64ToBlob(content) {
      let parts = content.split(';base64,')
      let contentType = parts[0].split(':')[1]
      let raw = window.atob(parts[1])
      let rawLength = raw.length

      let uInt8Array = new Uint8Array(rawLength)

      for (let i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i)
      }
      return new Blob([uInt8Array], { type: contentType })
    },
    /**
     * 手动上传文件列表
     * 暴露的API供外部调用
     */
    async submit() {
      // 未有文件改变，没有触发 onChange, onRemove 钩子
      if (!this.uploadList.length && !this.hasRemoveAction) {
        return this.doComplete(this.fileList)
      }

      // 是否全部调用完成（有fileList，然后只删除没添加时的情况）
      if (this.uploadList.every(item => item.status === 'success')) {
        return this.doComplete(this.uploadList)
      }
      // 有文件改变，调用 el-upload 的 submit api，手动开始上传
      // 把获取认证信息放在这里是为了避免批量上传，要同时请求多次认证的情况
      this.$children[0].submit()
    },

    handleDelete() {
      msgbox({
        message: `确定删除?`,
        beforeClose: () =>
          this.$http
            .delete(this.action)
            .then(() => {
              this.$message.success('删除成功')
              this.$emit('success', true)
            })
            .catch(() => this.$message.error('删除失败')),
      })
    },

    /**
     * 自定义上传行为，覆盖默认的上传行为
     * 因为用 el-upload 组件自身的 action，在firefox上会出现第一次为空的bug
     * 一个文件就会有一个上传行为，当多文件上传时，需要自己实现所有文件上传完成的监听
     */
    httpRequest(option) {
      const formData = new FormData()
      formData.append('file', option.file)
      formData.append('success_action_status', 200) // 让服务端返回200，不然，默认会返回204
      if (!this.isPut) {
        this.$http
          .post(this.action, formData, {
            onUploadProgress: e => {
              if (e.total > 0) {
                e.percent = (e.loaded / e.total) * 100
              }
              option.onProgress(e)
            },
          })
          .then(() => {
            this.$emit('success', true)
          })
          .catch(err => {
            this.$message.error('上传失败')
            option.onError(err)
          })
      } else {
        this.$http
          .put(this.action, formData, {
            onUploadProgress: e => {
              if (e.total > 0) {
                e.percent = (e.loaded / e.total) * 100
              }
              option.onProgress(e)
            },
          })
          .then(() => {
            this.$emit('success', true)
          })
          .catch(err => {
            this.$message.error('上传失败')
            option.onError(err)
          })
      }
    },

    /**
     * 所有提交之前的动作，比如压缩、命名等
     * 不能放在 onChange 钩子内，因为当自动上传时，不会等待onChange的同步、异步时间，会直接上传
     */
    async selfBeforeUpload(file) {
      const before = this.beforeUpload(file)
      if (before && before.then) {
        const beforeFile = await before.then()
        const fileType = Object.prototype.toString.call(beforeFile)
        if (fileType === '[object File]' || fileType === '[object Blob]') {
          return beforeFile
        }
        return file
      } else if (before !== false) {
        return file
      } else {
        return false
      }
    },

    /**
     * 文件状态改变时的钩子，添加文件、上传成功和上传失败时都会被调用
     */
    async selfOnChange(file, fileList) {
      // 当文件改变时，处理文件
      this.getSingleImageObj(fileList)
      if (file.status === 'ready') {
        // 单张图片时，只上传最后一张
        if (!this.showFileList) {
          for (let i = 0; i < this.uploadList.length - 1; i++) {
            this.uploadList[i].status = 'success'
          }
        }
      }

      this.uploadList = fileList
      this.onChange(file, fileList)
    },

    selfOnRemove(file, fileList) {
      this.uploadList = fileList
      this.hasRemoveAction = true
      this.onRemove(file, fileList)
    },

    handleSingleImageObjPreview() {
      this.$utils.viewer({
        images: this.singleImageObj,
        index: 0,
      })
    },

    /**
     * 每一个文件上传完成的回调
     */
    selfOnSuccess(res, file, fileList) {
      this.uploadList = fileList
      this.onSuccess(res, file, fileList)

      if (fileList.every(item => item.status === 'success')) {
        this.doComplete(fileList, file)
      }
    },

    /**
     * 预览
     */
    selfOnPreview(file) {
      let fileList = this.uploadList
      if (!fileList.length) {
        fileList = this.fileList || []
      }
      if (!this.showFileList) {
        fileList = fileList.slice(-1)
      }
      if (this.isImageType) {
        this.$utils.viewer({
          images: fileList,
          index: fileList.findIndex(item => item.uid === file.uid) || 0,
        })
      }

      this.onPreview(file, fileList)
    },

    getSingleImageObj(fileList = []) {
      const len = fileList.length
      this.singleImageObj = len ? fileList[len - 1] : {}
    },

    /**
     * 全部完成的回调处理
     */
    doComplete(fileList = []) {
      // showFileList为false时，只回调最后一张图片（之前也处理了只上传最后一张图片）
      const list = this.showFileList ? fileList : fileList.slice(-1)
      this.onComplete(
        list.map(item => ({
          name: item.name,
          status: item.status,
          url: item.url,
          size: item.size,
        }))
      )
    },
  },
}
</script>
<style lang="scss" scoped>
.single-image {
  width: 146px;
  height: 146px;
  position: absolute;
  top: 0;
  left: 0;
}
.single-wrap {
  position: absolute;
  width: 146px;
  height: 146px;
  &:hover,
  &:focus {
    .single-mask {
      opacity: 1;
    }
    .single-actions {
      display: block;
    }
  }
}
.single-mask {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  border-radius: 4px;
  background-color: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 0.3s;
}
.single-actions {
  position: absolute;
  text-align: center;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  top: -1px;
  display: none;
  transition: opacity 0.3s;
  .action-icon {
    color: #fff;
    font-size: 28px;
    width: 30px;
    height: 30px;
  }
  .action-warp {
    display: flex;
    flex-wrap: wrap;
    line-height: 73px;
  }
  .action-warp-icon {
    align-self: center;
    &:hover {
      background: $primary;
    }
    width: 50%;
  }
}
</style>
